﻿//===============================================================================
// Magic OQL
//===============================================================================
// Copyright ?Magic Thought Corporation. All rights reserved.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===============================================================================
// Richie (http://www.cnblogs.com/RicCC)
// March 7, 2008
//===============================================================================

using System.Collections.Generic;
using Antlr.Runtime;
using Antlr.Runtime.Tree;

namespace SQLAnalyticLibrary.Expressions
{
    /// <summary>
    /// Select statement:
    /// <para>SelectClause FromClause? WhereClause? GroupByClause? OrderByClause?</para>
    /// </summary>
    public class SelectStatement : QueryStatement
    {
        #region constructors
        /// <summary>
        /// SelectStmt is a imaginary token. Don't need combine subsequent tokens.
        /// </summary>
        /// <param name="node"></param>
        public SelectStatement(IToken t)
            : base(t)
        {
        }
        public SelectStatement(SelectStatement t)
            : base(t.Token)
        {
            this._children = t._children;
            this._columns = t._columns;
            this._isCompiled = t._isCompiled;
            this._positionedVariables = t._positionedVariables;
            this._subQueries = t._subQueries;
            this._tables = t._tables;
        }
        /// <summary>
        /// SelectStmt is a imaginary token. Don't need combine subsequent tokens.
        /// </summary>
        /// <param name="t"></param>
        public SelectStatement(IToken t, string text)
            : base(t, text)
        {
        }
        #endregion

        public override Expression DupNode()
        {
            return new SelectStatement(this);
        }

        public static bool IsSelectStatement(Expression exp)
        {
            if (exp == null || exp.IsNil) return false;
            return exp.Token.Type == OQLParser.SelectStmt;
        }

        public override void VisitSql(ISqlVisitor visitor)
        {
            RequiredClause clauseFlag = visitor.ClauseFlag;
            SqlVisitorInterceptor interceptor = new SqlVisitorInterceptor(visitor, this);
            if (!this.IsTopStatement && this.NeedParen)
                visitor.Sql("(");
            if ((clauseFlag & RequiredClause.All) == RequiredClause.All)
                this.InternalVisitSql(interceptor, true, " ");
            else
                for (int i = 0; this._children != null && i < this._children.Count; i++)
                {
                    if (SelectClause.IsSelectClause(this._children[i]) && (clauseFlag & RequiredClause.Select) == RequiredClause.Select)
                    {
                        if (i != 0) visitor.Space();
                        this._children[i].VisitSql(interceptor);
                    }
                    if (FromClause.IsFromClause(this._children[i]) && (clauseFlag & RequiredClause.From) == RequiredClause.From)
                    {
                        if (i != 0) visitor.Space();
                        this._children[i].VisitSql(interceptor);
                    }
                    if (WhereClause.IsWhereClause(this._children[i]) && (clauseFlag & RequiredClause.Where) == RequiredClause.Where)
                    {
                        if (i != 0) visitor.Space();
                        this._children[i].VisitSql(interceptor);
                    }
                    if (OrderByClause.IsOrderByClause(this._children[i]) && (clauseFlag & RequiredClause.OrderBy) == RequiredClause.OrderBy)
                    {
                        if (i != 0) visitor.Space();
                        this._children[i].VisitSql(interceptor);
                    }
                    if (GroupByClause.IsGroupByClause(this._children[i]) && (clauseFlag & RequiredClause.GroupBy) == RequiredClause.GroupBy)
                    {
                        if (i != 0) visitor.Space();
                        this._children[i].VisitSql(interceptor);
                    }
                }
            if (!this.IsTopStatement && this.NeedParen)
                visitor.Sql(")");
            interceptor.Close();
            interceptor = null;
        }
        public Expression GetWhereCondition()
        {
            WhereClause whereClause = null;
            foreach (Expression exp in this)
                if (WhereClause.IsWhereClause(exp)) { whereClause = exp as WhereClause; break; }
            if (whereClause == null) return null;
            return whereClause.WholeCondition();
        }
        public void ReplaceWhereCondition(Expression newCondition)
        {
            if (this._children == null) return;
            WhereClause whereClause = null;
            int index = -1;
            for (int i = 0; i < this._children.Count; i++)
                if (WhereClause.IsWhereClause(this._children[i]))
                {
                    index = i;
                    whereClause = this._children[i] as WhereClause;
                }
            if (newCondition == null) //remove the where clause? No, this will affect the compilation result.
                return;

            if (whereClause == null)
            {
                //where clause not exists in this select statement, create a new one.
                //TODO: if the query is compiled?
                whereClause = OqlUtil.CreateWhere();
                index = -1;
                for (int i = 0; i < this._children.Count; i++)
                    if (SelectClause.IsSelectClause(this._children[i]) || FromClause.IsFromClause(this._children[i])) index = i;
                if (index <= 0) return;
                index++;
                if (index >= this._children.Count) this._children.Add(whereClause);
                else
                    this._children.Insert(index, whereClause);
            }

            whereClause.ReplaceWholeCondition(newCondition);
        }
        public bool HasSelectClause
        {
            get
            {
                if (this._children == null || this._children.Count <= 0) return false;
                return SelectClause.IsSelectClause(this._children[0]);
            }
        }
        public bool HasWhereClause
        {
            get
            {
                if (this._children == null || this._children.Count <= 0) return false;
                foreach (Expression exp in this._children)
                    if (WhereClause.IsWhereClause(exp)) return true;
                return false;
            }
        }
        public string SelectStringNoColumns
        {
            get
            {
                if (this._children == null || this._children.Count <= 0) return null;
                if (!SelectClause.IsSelectClause(this._children[0])) return null;
                SelectClause clause = this._children[0] as SelectClause;
                return clause.ToString();
            }
        }
        public override int ColumnCountInSelect
        {
            get
            {
                SelectClause selectClause = null;
                foreach (Expression exp in this)
                    if (SelectClause.IsSelectClause(exp))
                        selectClause = exp as SelectClause;
                if (selectClause == null) return 0;
                return selectClause.Columns == null ? 0 : selectClause.Columns.Count;
            }
        }
    }
}
